package com.eagle.common.core.utils;

import lombok.NonNull;

import java.time.*;
import java.time.chrono.ChronoZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.WeekFields;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author wcy
 * @date Created in 2021/8/28 18:00 <br>
 */
public class LocalDateUtil {

    private static final String DEFAULT_FORMAT = "yyyy-MM-dd HH:mm:ss";

    private static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";

    private static final String DEFAULT_MONTH_DAY_FORMAT = "MM-dd";

    public static String getDateTimeAsString(@NonNull LocalDateTime localDateTime, String format) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);

        return localDateTime.format(formatter);
    }

    public static String getDateTimeAsString(@NonNull LocalDateTime localDateTime) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DEFAULT_FORMAT);

        return localDateTime.format(formatter);
    }

    public static String getDateAsString(@NonNull LocalDate localDate) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT);

        return localDate.format(formatter);
    }

    public static String getDateAsString(@NonNull LocalDate localDate, String format) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);

        return localDate.format(formatter);
    }

    public static String getMonthDayAsString(@NonNull LocalDate localDate) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DEFAULT_MONTH_DAY_FORMAT);

        return localDate.format(formatter);
    }

    /**
     * 将时间戳转为LocalDateTime
     *
     * @param timestamp
     * @return
     */
    public static LocalDateTime getDateTimeOfTimestamp(long timestamp) {
        Instant instant = Instant.ofEpochMilli(timestamp);
        ZoneId zone = ZoneId.systemDefault();

        return LocalDateTime.ofInstant(instant, zone);
    }

    /**
     * 将LocalDateTime转为时间戳
     *
     * @param localDateTime
     * @return
     */
    public static long getTimestampOfDateTime(LocalDateTime localDateTime) {
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = localDateTime.atZone(zone).toInstant();

        return instant.toEpochMilli();
    }

    public static Date toDate(LocalDateTime localDateTime) {
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = localDateTime.atZone(zone).toInstant();

        return Date.from(instant);
    }

    public static LocalDateTime fromDate(Date date) {
        if (date == null) {
            return null;
        }

        return getDateTimeOfTimestamp(date.getTime());
    }

    public static long getSecondInterval(LocalDateTime start, LocalDateTime end) {
        Duration duration = Duration.between(start, end);

        return duration.getSeconds();
    }

    public static LocalDateTime parseStringToDateTime(String time, String format) {
        DateTimeFormatter df = DateTimeFormatter.ofPattern(format);
        return LocalDateTime.parse(time, df);
    }

    public static LocalDate parseStringToDate(String time) {
        DateTimeFormatter df = DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT);
        return LocalDate.parse(time, df);
    }

    public static LocalDate parseStringToDate(String time, String format) {
        DateTimeFormatter df = DateTimeFormatter.ofPattern(format);
        return LocalDate.parse(time, df);
    }

    /**
     * 2018-08-30 16:36:00 -> LocalDateTime 默认的
     *
     * @param time
     * @return
     */
    public static LocalDateTime parseStringToDateTime(String time) {
        DateTimeFormatter df = DateTimeFormatter.ofPattern(DEFAULT_FORMAT);
        return LocalDateTime.parse(time, df);
    }

    public static LocalDateTime getDayStart(LocalDateTime time) {
        return time.withHour(0)
                .withMinute(0)
                .withSecond(0)
                .withNano(0);
    }


    public static LocalDateTime getDayEnd(LocalDateTime time) {
        return time.withHour(23)
                .withMinute(59)
                .withSecond(59)
                .withNano(999999999);
    }


    public static LocalDateTime getHourTime(LocalDateTime time) {
        return time.withMinute(0)
                .withSecond(0)
                .withNano(0);
    }


    public static LocalDateTime getHourEndTime(LocalDateTime time) {
        return time.withMinute(59)
                .withSecond(59)
                .withNano(99999);
    }

    public static LocalDateTime getDayStart(LocalDate date) {
        return LocalDateTime.of(date, LocalTime.MIN);
    }

    public static LocalDateTime getDayEnd(LocalDate date) {
        return LocalDateTime.of(date, LocalTime.MAX).withNano(999);
    }

    public static LocalDate getMonthStartDay(LocalDate date) {
        return date.with(TemporalAdjusters.firstDayOfMonth());
    }

    public static LocalDate getMonthEndDay(LocalDate date) {
        return date.with(TemporalAdjusters.lastDayOfMonth());
    }

    public static LocalDate getYearStartDay(LocalDate date) {
        return date.with(TemporalAdjusters.firstDayOfYear());
    }

    public static LocalDate getYearEndDay(LocalDate date) {
        return date.with(TemporalAdjusters.lastDayOfYear());
    }

    public static long diffDay(LocalDateTime startTime, LocalDateTime endTime) {
        return endTime.toLocalDate().toEpochDay() - startTime.toLocalDate().toEpochDay();
    }

    public static int diffMinutes(LocalDateTime startTime, LocalDateTime endTime) {
        return endTime.toLocalTime().getMinute() - startTime.toLocalTime().getMinute();
    }

    public static int diffHour(LocalDateTime startTime, LocalDateTime endTime) {
        return endTime.toLocalTime().getHour() - startTime.toLocalTime().getHour();
    }

    public static long diffDay(LocalDate startDate, LocalDate endDate) {
        return endDate.toEpochDay() - startDate.toEpochDay();
    }

    public static long diffMonth(LocalDate startDate, LocalDate endDate) {
        return startDate.until(endDate, ChronoUnit.MONTHS);
    }

    public static long diffMonth(LocalDateTime startTime, LocalDateTime endTime) {
        return startTime.until(endTime, ChronoUnit.MONTHS);
    }

    public static List<LocalDateTime> continuousTime(LocalDateTime endTime) {
        List<LocalDateTime> continuousTime = new ArrayList<>();
        LocalDateTime startTime = getDayStart(endTime);
        int hour = diffHour(startTime, endTime);
        for (int i = 0; i < hour + 1; i++) {
            LocalDateTime now = getHourTime(startTime).plusHours(i);
            continuousTime.add(now);
        }
        return continuousTime;
    }

    public static List<LocalDate> continuousDate(LocalDate startDate, LocalDate endDate) {
        List<LocalDate> continuousDate = new ArrayList<>();
        long day = diffDay(startDate, endDate);
        for (long i = 0L; i < day + 1L; i++) {
            LocalDate now = startDate.plusDays(i);
            continuousDate.add(now);
        }

        return continuousDate;
    }

    public static LocalDate dateToLocalDate(Date date) {
        Instant instant = date.toInstant();
        ZoneId zoneId = ZoneId.systemDefault();
        LocalDate localPriceDate = instant.atZone(zoneId).toLocalDate();

        return localPriceDate;
    }

    public static Integer getNaturalWeek(LocalDate day) {
        Integer naturalWeek = 0;
        if (day != null) {
            WeekFields weekFields = WeekFields.of(DayOfWeek.SUNDAY, 4);
            naturalWeek = day.get(weekFields.weekOfWeekBasedYear());
        }

        return naturalWeek;
    }

    public static Integer getNaturalMouth(LocalDate day) {
        Integer naturalMouth = 0;
        if (day != null) {
            naturalMouth = day.getMonth().getValue();
        }

        return naturalMouth;
    }

    public static LocalDate getWeekStartDay(LocalDate day) {
        return day.with(TemporalAdjusters.ofDateAdjuster(localDate -> localDate.minusDays(localDate.getDayOfWeek().getValue() - DayOfWeek.SUNDAY.getValue())));
    }

    public static LocalDate getWeekEndDay(LocalDate day) {
        return day.with(TemporalAdjusters.ofDateAdjuster(localDate -> localDate.plusDays(7 + DayOfWeek.SATURDAY.getValue() - localDate.getDayOfWeek().getValue())));
    }

    /**
     * 获取自然月多少天
     *
     * @param mouth
     * @return
     */
    public static Integer getDayNumOfMonth(Integer mouth) {
        return LocalDate.now().withMonth(mouth).lengthOfMonth();
    }


    public static Date localDateToDate(LocalDate localDate) {
        ZoneId zoneId = ZoneId.systemDefault();
        ChronoZonedDateTime<LocalDate> zonedDateTime = localDate.atStartOfDay(zoneId);

        return Date.from(zonedDateTime.toInstant());

    }

    public static String getMinDateTimeAsString(LocalDate localDate) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT);

        return localDate.format(formatter) + " 00:00:00";
    }

    public static String getMaxDateTimeAsString(LocalDate localDate) {
        if (localDate == null) {
            return null;
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT);
        return localDate.format(formatter) + " 23:59:59";
    }

    public static LocalDateTime getMinDateTime(LocalDate localDate) {
        if (localDate == null) {
            return null;
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT);
        return parseStringToDateTime(localDate.format(formatter) + " 00:00:00");
    }

    public static LocalDateTime getMaxDateTime(LocalDate localDate) {
        if (localDate == null) {
            return null;
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT);
        return parseStringToDateTime(localDate.format(formatter) + " 23:59:59");
    }
}
